/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       adv_shipyard.cpp

	$Header: /game/adv_shipyard.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "adv_shipyard.h"

#include "adv_object_type.h"
#include "adventure_ai.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_path.h"
#include "adventure_path_finder.h"
#include "adventure_sounds.h"
#include "army.h"
#include "basic_dialog.h"
#include "caravan.h"
#include "enum_operations.h"
#include "footprint.h"
#include "map_rect.h"
#include "material_array.h"
#include "music.h"
#include "object_registration.h"
#include "path_search_type.h"
#include "player.h"
#include "player_color.h"
#include "replace_keywords.h"
#include "simple_dialog.h"
#include "sized_int_types.h"
#include "streambuf_operators.h"


// ---------------------------------------------------------------
// unnamed namespace
// ---------------------------------------------------------------
namespace
{
	int const k_placement_search_range		= 2;	// How far to look for a place to put a ship

	int const k_current_map_format_version	= 2;

	t_object_registration< t_adv_shipyard > g_shipyard_registration( k_adv_object_shipyard );

	// Translate t_adv_shipyard's map version to
	// t_owned_scriptable_adv_object's map version
	inline int to_base_class_map_format_version( int format_version )
	{
		return format_version;
	}
} // unnamed namespace

// ---------------------------------------------------------------
// shipyard adventure object
// ---------------------------------------------------------------
bool t_adv_shipyard::read_from_map( std::streambuf& buffer, t_progress_handler* handler )
{
	int format_version = get< t_uint16 >( buffer );
	if ( format_version < 0 || format_version > k_current_map_format_version )
		return false;

	return t_owned_scriptable_adv_object::read_from_map( buffer, handler, to_base_class_map_format_version( format_version ) );
}

// ---------------------------------------------------------------
// shipyard adventure object
// ---------------------------------------------------------------
void t_adv_shipyard::build_ship( t_town_type alignment )
{
	t_player*			owner = get_owner();
	t_adv_map_point		position;
	t_material_array	boat_cost;

	boat_cost[k_gold] = 1000;
	boat_cost[k_wood] = 10;

	if (owner->is_computer())
	{
		if ((owner->get_funds() < boat_cost) || !find_new_ship_position( position ))
			return;
	}
	else
	{
		// cost of 1 boat
		std::string						text;
		t_counted_ptr<t_basic_dialog>	dialog;
		bool							can_buy = false;

		dialog = new t_basic_dialog;
		if (!find_new_ship_position( position ))
		{
			text = get_text( "full" );
		}
		else
		{
			dialog->add_materials( boat_cost );
			if (owner->get_funds() < boat_cost)
			{
				text = get_text( "denied" );
			} 
			else
			{
				text = get_text( "initial" );
				dialog->add_cancel_button();
				can_buy = true;
			}
			text = replace_keywords( text, boat_cost );
		}

		t_adv_map_point point;

		get_trigger_cell(  point );
		dialog->add_ok_button();
		dialog->set_text( text );
		dialog->open( get_dialog_bottom( point ), true, k_align_bottom );
		t_sound_cache music = get_music_playing();
		stop_music();
		get_dialog_sound( k_dialog_sound_shipyard )->play( get_sound_volume() );
		if (!dialog->run_modal())
		{
			play_music( music );
			return;
		}
		play_music( music );
		if (!can_buy)
			return;
	}

	t_army*				boat = new t_army( alignment );
	t_adventure_frame*	frame = get_adventure_frame();

	boat->set_owner( get_owner_number() );
	boat->place( *get_map(), position );
	owner->spend( boat_cost );
	frame->reset_pathfinder();
	frame->update();
}

// ---------------------------------------------------------------
// shipyard adventure object
// ---------------------------------------------------------------
void t_adv_shipyard::activate_trigger( t_army*		army,	   t_adv_map_point const&	point, 
									   t_direction	direction, t_adventure_frame*	frame )
{
	if ( !same_team( army->get_owner(), get_owner() ) )
	{
		if (army->get_living_hero_count() == 0)
		{
			if (army->is_human())
			{
				ok_dialog( get_text( "no_heroes" ), true );
			}
			return;
		}
		set_owner( army->get_owner_number() );
	}

	t_player* owner = army->get_owner();

	if (owner == 0)
		return;
	build_ship( army->get_creatures().get_leader().get_alignment() );
}

void t_adv_shipyard::left_double_click( t_mouse_event const& event, 
						                    t_adventure_frame* adventure_frame )
{
	t_adventure_map* map = get_map();

	if (map->get_current_player_number() != map->get_player_number()
		|| map->get_current_player_number() != get_owner_number())
		return;
	build_ship( get_owner()->get_alignment() );
}

// ---------------------------------------------------------------
// shipyard adventure object
// Helper function to check all points in a rect
// ---------------------------------------------------------------
static bool find_new_ship_position( t_adventure_map const * map, 
									t_adv_map_point & result,
									t_map_rect_2d const& rect )
{
	for (result.row = rect.top(); result.row < rect.bottom(); ++result.row)
	{
		for (result.column = rect.left(); result.column < rect.right(); ++result.column)
		{
			if ( map->can_place_new_boat( result, false ) )
				return true;
		}
	}
	return false;
}

// ---------------------------------------------------------------
// Determine start position for a boat.
// Strongly related to t_town::find_new_ship_position()
// Check all potential ship locations within range of our 
// building footprint.  Rectangles are arranged to give preference
// to more "visible" (ie: southerly) locations.
// You'll have to draw a picture on graph paper or just trust me.
// The positive row axis is down, and the positive column axis is
// right.  The 0,0 corner is "North" when drawn (ie rotated
// 45 degrees)
// ---------------------------------------------------------------
bool t_adv_shipyard::find_new_ship_position( t_adv_map_point & result ) const
{
	t_map_rect_2d		 rect;
	t_adv_map_point      position = get_position();
	t_map_point_2d       footprint_size = get_footprint().get_size();

	result.level = get_position().level;
	result.on_bridge = false;

	// search South/West edge (bottom with regard to row/column axes)
	rect.top_left.row = position.row + footprint_size.row;
	rect.top_left.column = position.column - k_placement_search_range;
	rect.bottom_right.row = rect.top_left.row + k_placement_search_range;
	rect.bottom_right.column = position.column + footprint_size.column + k_placement_search_range;
	if (::find_new_ship_position( m_map, result, rect ))
		return true;

	// search South/East edge (right with regard to row/column axes)
	rect.top_left.row = position.row - k_placement_search_range;
	rect.top_left.column = position.column + footprint_size.column;
	rect.bottom_right.row = position.row + footprint_size.row;
	rect.bottom_right.column = rect.top_left.column + k_placement_search_range;
	if (::find_new_ship_position( m_map, result, rect ))
		return true;

	// search North/West edge (left with regard to row/column axes)
	rect.top_left.row = position.row;
	rect.top_left.column = position.column - k_placement_search_range;
	rect.bottom_right.row = position.row + footprint_size.row;
	rect.bottom_right.column = position.column;
	if (::find_new_ship_position( m_map, result, rect ))
		return true;

	// search North/East edge (top with regard to row/column axes)
	rect.top_left.row = position.row - k_placement_search_range;
	rect.top_left.column = position.column - k_placement_search_range;
	rect.bottom_right.row = position.row;
	rect.bottom_right.column = position.column + footprint_size.column;
	if (::find_new_ship_position( m_map, result, rect ))
		return true;

	return false;
}

void t_adv_shipyard::read_postplacement( t_adventure_map& map )
{
	t_owned_scriptable_adv_object::read_postplacement( map );

	map.add_shipyard(*this);
}

bool t_adv_shipyard::preplacement( t_adventure_map& map, int pass )
{
	if (pass == 0) 
		map.add_shipyard(*this);
	
	return t_owned_scriptable_adv_object::preplacement( map, pass );
}

void t_adv_shipyard::on_removed()
{
	get_map()->remove_shipyard(*this);
	t_owned_scriptable_adv_object::on_removed();
}

// -------------------------------------------------------------------------------
// Add the points in the given rectangle to the list of adventure path points
// -------------------------------------------------------------------------------
static void add_pathing_destinations( t_adventure_map const * map, 
									  int level,
									  t_adventure_path_point const& source,
									  t_adventure_path_finder & path_finder,
									  t_map_rect_2d const& rect )
{
	t_adv_map_point new_point;

	new_point.on_bridge = false;
	new_point.level = level;
	for (new_point.row = rect.top(); new_point.row < rect.bottom(); ++new_point.row)
	{
		for (new_point.column = rect.left(); new_point.column < rect.right(); ++new_point.column)
		{
			if ( map->can_place_new_boat( new_point, false ) )
			{
				path_finder.push( new_point, source, source.move_remaining, 0, 0.0f, k_army_path_state_ship );
			}
		}
	}
}

// -------------------------------------------------------------------------------
// virtual function used by the pathfinder to determine if an object
// should place additonal points into the path search.
// ---------------------------------------------------------------
// Largly dupplicate of find_new_ship_position, but instead
// this places all the points a ship could be built into
// the path finders list of reachable points.
// BE SURE TO KEEP THIS IN SYNC WITH t_town::find_new_ship_position()
// -------------------------------------------------------------------------------
void t_adv_shipyard::pathing_destination_query( t_adventure_path_point const& source, 
												t_adventure_path_finder & path_finder ) const
{
	t_path_search_type type = path_finder.get_path_type();

	if ((type == k_path_search_ai) && !same_team(get_owner(), path_finder.get_army()->get_owner()))
	{
		t_creature_array& army = *path_finder.get_army();
	
		if (army.get_living_hero_count() == 0)
			return;

		t_adventure_tile const& tile = get_map()->get_adv_tile( source );
		t_adv_map_point ship_position;
		if (find_new_ship_position( ship_position ))
			path_finder.push( ship_position, source, source.move_remaining, 0, get_ai()->get_ship_construction_barrier(), k_army_path_state_ship );

	}
	else if (type == k_path_search_caravan)
	{	

		// Special handling for caravans -- the player's shipyards act as a zero-resource-cost gateway to 
		// the water squares.
		// This part is largly copies from find_new_ship_position above and should remain in sync with it.
		
		if ( source.pathing_state != k_army_path_state_normal )
			return;

		if ( !same_team( path_finder.get_army()->get_owner(), get_owner() ) )
			return;

		t_map_rect_2d		 rect;
		t_level_map_point_2d position = get_position();
		t_map_point_2d       footprint_size = get_footprint().get_size();


		// search South/West edge (bottom with regard to row/column axes)
		rect.top_left.row = position.row + footprint_size.row;
		rect.top_left.column = position.column - k_placement_search_range;
		rect.bottom_right.row = rect.top_left.row + k_placement_search_range;
		rect.bottom_right.column = position.column + footprint_size.column + k_placement_search_range;
		::add_pathing_destinations( m_map, position.level, source, path_finder, rect );

		// search South/East edge (right with regard to row/column axes)
		rect.top_left.row = position.row - k_placement_search_range;
		rect.top_left.column = position.column + footprint_size.column;
		rect.bottom_right.row = position.row + footprint_size.row;
		rect.bottom_right.column = rect.top_left.column + k_placement_search_range;
		::add_pathing_destinations( m_map, position.level, source, path_finder, rect );

		// search North/West edge (left with regard to row/column axes)
		rect.top_left.row = position.row;
		rect.top_left.column = position.column - k_placement_search_range;
		rect.bottom_right.row = position.row + footprint_size.row;
		rect.bottom_right.column = position.column;
		::add_pathing_destinations( m_map, position.level, source, path_finder, rect );

		// search North/East edge (top with regard to row/column axes)
		rect.top_left.row = position.row - k_placement_search_range;
		rect.top_left.column = position.column - k_placement_search_range;
		rect.bottom_right.row = position.row;
		rect.bottom_right.column = position.column + footprint_size.column;
		::add_pathing_destinations( m_map, position.level, source, path_finder, rect );

	}
}

